home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 3: Developer Tools / Linux Cubed Series 3 - Developer Tools.iso / utils / disk-man / mtools-3.000 / mtools-3 / mtools-3.0 / mk_direntry.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-05-08  |  15.4 KB  |  658 lines

  1. /*
  2.  * mk_direntry.c
  3.  * Make new directory entries, and handles name clashes
  4.  *
  5.  */
  6.  
  7. /*
  8.  * This file is used by those commands that need to create new directory entries
  9.  */
  10.  
  11. #include "sysincludes.h"
  12. #include "msdos.h"
  13. #include "mtools.h"
  14. #include "vfat.h"
  15. #include "nameclash.h"
  16. #include "fs.h"
  17.  
  18. static inline int ask_rename(ClashHandling_t *ch,
  19.                  char *longname, int isprimary, char *argname)
  20. {
  21.     char shortname[13];
  22.     int mangled;
  23.  
  24.     /* TODO: Would be nice to suggest "autorenamed" version of name, press 
  25.      * <Return> to get it.
  26.      */
  27. #if 0
  28.     fprintf(stderr,"Entering ask_rename, isprimary=%d.\n", isprimary);
  29. #endif
  30.  
  31.     if(!opentty(0))
  32.         return 0;
  33.  
  34. #define maxsize (isprimary ?  MAX_VNAMELEN+1 : 11+1)
  35. #define name (isprimary ? argname : shortname)
  36.  
  37.     mangled = 0;
  38.     do {
  39.         fprintf(stderr, "New %s name for \"%s\": ",
  40.             isprimary ? "primary" : "secondary", longname);
  41.         fflush(stderr);
  42.         if (! fgets(name, maxsize, opentty(0)))
  43.             return 0;
  44.  
  45.         /* Eliminate newline(s) in the file name */
  46.         name[strlen(name)-1]='\0';
  47.         if (!isprimary)
  48.             ch->name_converter(shortname,0, &mangled, argname);
  49.     } while (mangled & 1);
  50.     return 1;
  51. #undef maxsize
  52. #undef name
  53. }
  54.  
  55. static inline clash_action ask_namematch(char *name, int isprimary, 
  56.                      ClashHandling_t *ch, int no_overwrite,
  57.                      int reason)
  58. {
  59.     char ans[10];
  60.     clash_action a;
  61.     int perm;
  62.     char unix_shortname[13];
  63.  
  64.  
  65. #define EXISTS 0
  66. #define RESERVED 1
  67. #define ILLEGALS 2
  68.  
  69.     static char *reasons[]= {
  70.         "already exists",
  71.         "is reserved",
  72.         "contains illegal character(s)"};
  73.  
  74.  
  75.     if (!isprimary)
  76.         name = unix_normalize(unix_shortname, name, name+8);
  77.  
  78.     a = ch->action[isprimary];
  79.  
  80.     if(!opentty(1)) {
  81.         /* no default, and no tty either . Skip the troublesome file */
  82.         if(a == NAMEMATCH_NONE)
  83.             a = NAMEMATCH_SKIP;
  84.         return a;
  85.     }
  86.  
  87.     perm = 0;
  88.     while (a == NAMEMATCH_NONE) {
  89.         fprintf(stderr, "%s file name \"%s\" %s.\n",
  90.             isprimary ? "Long" : "Short", name, reasons[reason]);
  91.         fprintf(stderr,
  92.             "a)utorename A)utorename-all r)ename R)ename-all ");
  93.         if(!no_overwrite)
  94.             fprintf(stderr,"o)verwrite O)verwrite-all");
  95.         fprintf(stderr,
  96.             "\ns)kip S)kip-all q)uit (aArR");
  97.         if(!no_overwrite)
  98.             fprintf(stderr,"oO");
  99.         fprintf(stderr,"sSq): ");
  100.         fflush(stderr);
  101.         fflush(opentty(1));
  102.         if (mtools_raw_tty) {
  103.             ans[0] = fgetc(opentty(1));
  104.             fputs("\n", stderr);
  105.         } else {
  106.             fgets(ans, 9, opentty(0));
  107.         }
  108.         perm = isupper(ans[0]);
  109.         switch(tolower(ans[0])) {
  110.             case 'a':
  111.                 a = NAMEMATCH_AUTORENAME;
  112.                 break;
  113.             case 'r':
  114.                 if(isprimary)
  115.                     a = NAMEMATCH_PRENAME;
  116.                 else
  117.                     a = NAMEMATCH_RENAME;
  118.                 break;
  119.             case 'o':
  120.                 if(no_overwrite)
  121.                     continue;
  122.                 a = NAMEMATCH_OVERWRITE;
  123.                 break;
  124.             case 's':
  125.                 a = NAMEMATCH_SKIP;
  126.                 break;
  127.             case 'q':
  128.                 perm = 0;
  129.                 a = NAMEMATCH_QUIT;
  130.                 break;
  131.             default:
  132.                 perm = 0;
  133.         }
  134.     }
  135.  
  136.     /* Keep track of this action in case this file collides again */
  137.     ch->action[isprimary]  = a;
  138.     if (perm)
  139.         ch->namematch_default[isprimary] = a;
  140.  
  141.     /* if we were asked to overwrite be careful. We can't set the action
  142.      * to overwrite, else we get won't get a chance to specify another
  143.      * action, should overwrite fail. Indeed, we'll be caught in an
  144.      * infinite loop because overwrite will fail the same way for the
  145.      * second time */
  146.     if(a == NAMEMATCH_OVERWRITE)
  147.         ch->action[isprimary] = NAMEMATCH_NONE;
  148.     return a;
  149. }
  150.  
  151. /* Returns:
  152.  * 2 if file is to be overwritten
  153.  * 1 if file was renamed
  154.  * 0 if it was skipped
  155.  *
  156.  * If a short name is involved, handle conversion between the 11-character
  157.  * fixed-length record DOS name and a literal null-terminated name (e.g.
  158.  * "COMMAND  COM" (no null) <-> "COMMAND.COM" (null terminated)).
  159.  *
  160.  * Also, immediately copy the original name so that messages can use it.
  161.  */
  162. static inline clash_action process_namematch(char *name,
  163.                          char *longname,
  164.                          int isprimary,
  165.                          ClashHandling_t *ch,
  166.                          int no_overwrite,
  167.                          int reason)
  168. {
  169.     clash_action action;
  170.  
  171. #if 0
  172.     fprintf(stderr,
  173.         "process_namematch: name=%s, default_action=%d, ask=%d.\n",
  174.         name, default_action, ch->ask);
  175. #endif
  176.  
  177.     action = ask_namematch(name, isprimary, ch, no_overwrite, reason);
  178.  
  179.     switch(action){
  180.     case NAMEMATCH_QUIT:
  181.         got_signal = 1;
  182.         return NAMEMATCH_SKIP;
  183.     case NAMEMATCH_SKIP:
  184.         return NAMEMATCH_SKIP;
  185.     case NAMEMATCH_RENAME:
  186.     case NAMEMATCH_PRENAME:
  187.         /* We need to rename the file now.  This means we must pass
  188.          * back through the loop, a) ensuring there isn't a potential
  189.          * new name collision, and b) finding a big enough VSE.
  190.          * Change the name, so that it won't collide again.
  191.          */
  192.         ask_rename(ch, longname, isprimary, name);
  193.         return action;
  194.     case NAMEMATCH_AUTORENAME:
  195.         /* Very similar to NAMEMATCH_RENAME, except that we need to
  196.          * first generate the name.
  197.          * TODO: Remember previous name so we don't
  198.          * keep trying the same one.
  199.          */
  200.         if (isprimary) {
  201.             autorename_long(name, 1);
  202.             return NAMEMATCH_PRENAME;
  203.         } else {
  204.             autorename_short(name, 1);
  205.             return NAMEMATCH_RENAME;
  206.         }
  207.     case NAMEMATCH_OVERWRITE:
  208.         if(no_overwrite)
  209.             return NAMEMATCH_SKIP;
  210.         else
  211.             return NAMEMATCH_OVERWRITE;
  212.     default:
  213.         return NAMEMATCH_NONE;
  214.     }
  215. }
  216.  
  217.  
  218. static void clear_scan(char *longname, int use_longname, struct scan_state *s)
  219. {
  220.     s->shortmatch = s->longmatch = s->slot = -1;
  221.     s->free_size = s->got_slots = s->free_start = 0;
  222.  
  223.     if (use_longname & 1)
  224.                 s->size_needed = 2 + (strlen(longname)/VSE_NAMELEN);
  225.     else
  226.                 s->size_needed = 1;
  227. }
  228.  
  229.  
  230. static int scan_dir(Stream_t *Dir,
  231.             char *dosname,
  232.             char *longname,
  233.             struct vfat_state *vsp,
  234.             struct scan_state *ssp, 
  235.             int use_longname,
  236.             int ignore_entry)
  237. {
  238.     int entry;
  239.     struct directory dir;
  240.     int vse_start;
  241.     int ignore_match;
  242.     int ret;
  243.     int address;
  244.     char readlongname[VBUFSIZE];
  245.     char readshortname[13];
  246.     ignore_match = (ignore_entry == -2 );
  247.  
  248.     vse_start = -1;
  249.     clear_scan(longname, use_longname, ssp);
  250.     clear_vfat(vsp);
  251.     entry = 0;
  252.     ssp->match_free = 0;
  253.     while(1){
  254.         if(!ssp->got_slots){
  255.             ssp->free_start = entry;
  256.             ssp->free_size = 0;
  257.         }
  258.         if (ssp->longmatch > -1)
  259.             return 1; /* Name match, process and try again */
  260.         ret=vfat_lookup(Dir, 0, &dir, &entry, &vse_start, 0,
  261.                 ACCEPT_PLAIN | ACCEPT_DIR | ACCEPT_LABEL |
  262.                 MATCH_ANY,
  263.                 0, readshortname, readlongname, 0);
  264.         if(!ssp->got_slots){
  265.             ssp->free_size = vse_start - ssp->free_start;
  266.             if(ssp->free_size >= ssp->size_needed){
  267.                 /* enough entries */
  268.                 ssp->got_slots = 1;
  269.                 ssp->slot = ssp->free_start + 
  270.                     ssp->size_needed - 1;
  271.             }
  272.         }
  273.         if(!ssp->got_slots && ssp->slot == -1 && ssp->free_size > 1)
  274.             /* If there aren't enough consecutive entries
  275.              * to store the long file name, perhaps just
  276.              * store it with the short name? 
  277.              */
  278.             ssp->slot = ssp->free_start;
  279.         if(ret)
  280.             break;
  281.  
  282.         /* labels never match, neither does the ignored entry */
  283.         if( (dir.attr & 0x8) || (entry - 1 == ignore_entry) )
  284.             continue;
  285.         
  286.         /* check long name */
  287.         if((*readlongname && 
  288.             !strncasecmp(readlongname, longname, VBUFSIZE-1)) ||
  289.            (*readshortname &&
  290.             !strncasecmp(readshortname, longname, VBUFSIZE-1)))
  291.             ssp->longmatch = entry - 1;
  292.  
  293.         /* Long name or not, always check for short name match */
  294.         if (!ignore_match &&
  295.             !strncasecmp(dosname, dir.name, 8) &&
  296.             !strncasecmp(dosname+8, dir.ext, 3))
  297.             ssp->shortmatch = entry - 1;
  298.     }
  299.  
  300.     if (ssp->shortmatch > -1)
  301.         return 1;
  302.     ssp->max_entry = vse_start;
  303.     if (ssp->got_slots)
  304.         return 6;    /* Success */
  305.  
  306.     /* Need more room.  Can we grow the directory? */
  307.     GET_DATA(Dir,0,0,0,&address);
  308.     if (address)
  309.         return 5;    /* OK, try to grow the directory */
  310.  
  311.     /* no '.' entry means root directory */
  312.     fprintf(stderr, "No directory slots\n");
  313.     return -1;
  314.  
  315. } /* scan_dir */
  316.  
  317.  
  318. static int contains_illegals(char *string, char *illegals)
  319. {
  320.     for(; *string ; string++)
  321.         if((*string < ' ' && *string != '\005' && !(*string & 0x80)) ||
  322.            strchr(illegals, *string))
  323.             return 1;
  324.     return 0;
  325. }
  326.  
  327. static int is_reserved(char *ans, int islong)
  328. {
  329.     int i;
  330.     static char *dev3[] = {"CON", "AUX", "PRN", "NUL", "   "};
  331.     static char *dev4[] = {"COM", "LPT" };
  332.  
  333.     for (i = 0; i < sizeof(dev3)/sizeof(*dev3); i++)
  334.         if (!strncasecmp(ans, dev3[i], 3) &&
  335.             ((islong && !ans[3]) ||
  336.              (!islong && !strncmp(ans+3,"     ",5))))
  337.             return 1;
  338.  
  339.     for (i = 0; i < sizeof(dev4)/sizeof(*dev4); i++)
  340.         if (!strncasecmp(ans, dev4[i], 3) &&
  341.             (ans[3] >= '1' && ans[3] <= '4') &&
  342.             ((islong && !ans[4]) ||
  343.              (!islong && !strncmp(ans+4,"    ",4))))
  344.             return 1;
  345.     
  346.     return 0;
  347. }
  348.  
  349. static inline clash_action get_slots(Stream_t *Dir,
  350.                      Stream_t *Fs,
  351.                      char *dosname, char *longname,
  352.                      struct scan_state *ssp,
  353.                      ClashHandling_t *ch)
  354. {
  355.     struct vfat_state vfat;
  356.     clash_action ret;
  357.     int match=0;
  358.     struct directory dir;
  359.     int isprimary;
  360.     int no_overwrite;
  361.     int reason;
  362.  
  363.     no_overwrite = 1;
  364.     if((is_reserved(longname,1)) ||
  365.        longname[strspn(longname,". ")] == '\0'){
  366.         reason = RESERVED;
  367.         isprimary = 1;
  368.     } else if(contains_illegals(longname,long_illegals)) {
  369.         reason = ILLEGALS;
  370.         isprimary = 1;
  371.     } else if(is_reserved(dosname,0)) {
  372.         reason = RESERVED;
  373.         ch->use_longname = 1;
  374.         isprimary = 0;
  375.     } else if(contains_illegals(dosname,short_illegals)) {
  376.         reason = ILLEGALS;
  377.         ch->use_longname = 1;
  378.         isprimary = 0;
  379.     } else {
  380.         reason = EXISTS;
  381.         switch (scan_dir(Dir, dosname, longname, &vfat, 
  382.                  ssp, ch->use_longname, ch->ignore_entry)) {
  383.             case -1:
  384.                 return NAMEMATCH_ERROR;
  385.                 
  386.             case 0:
  387.                 return NAMEMATCH_SKIP; 
  388.                 /* Single-file error error or skip request */
  389.                 
  390.             case 5:
  391.                 return NAMEMATCH_GREW;
  392.                 /* Grew directory, try again */
  393.                 
  394.             case 6:
  395.                 return NAMEMATCH_SUCCESS; /* Success */
  396.         }        
  397.         match = -1;
  398.         if (ssp->longmatch > -1) {
  399.             /* Primary Long Name Match */
  400. #ifdef debug
  401.             fprintf(stderr,
  402.                 "Got longmatch=%d for name %s.\n", 
  403.                 longmatch, longname);
  404. #endif            
  405.             match = ssp->longmatch;
  406.             isprimary = 1;
  407.         } else if ((ch->use_longname & 1) && (ssp->shortmatch > -1)) {
  408.             /* Secondary Short Name Match */
  409. #ifdef debug
  410.             fprintf(stderr,
  411.                 "Got secondary short name match for name %s.\n", 
  412.                 longname);
  413. #endif
  414.  
  415.             match = ssp->shortmatch;
  416.             isprimary = 0;
  417.         } else if (ssp->shortmatch >= 0) {
  418.             /* Primary Short Name Match */
  419. #ifdef debug
  420.             fprintf(stderr,
  421.                 "Got primary short name match for name %s.\n", 
  422.                 longname);
  423. #endif
  424.             match = ssp->shortmatch;
  425.             isprimary = 1;
  426.         } else 
  427.             return NAMEMATCH_RENAME;
  428.         
  429.         dir_read(Dir, &dir, match, NULL);
  430.         /* if we can't overwrite, don't propose it */
  431.         no_overwrite = (match == ch->source || (dir.attr & 0x10));
  432.     }
  433.     ret = process_namematch(isprimary ? longname : dosname, longname,
  434.                 isprimary, ch, no_overwrite, reason);
  435.     
  436.     if (ret == NAMEMATCH_OVERWRITE && match > -1){
  437.         if((dir.attr & 0x5) &&
  438.            (ask_confirmation("file is read only, overwrite anyway (y/n) ? ",0,0)))
  439.             return NAMEMATCH_RENAME;
  440.         
  441.         /* Free up the file to be overwritten */
  442.         if (fat_free(Fs,_WORD(dir.start)))
  443.             return NAMEMATCH_ERROR;
  444.         
  445. #if 0
  446.         if(isprimary &&
  447.            match - ssp->match_free + 1 >= ssp->size_needed){
  448.             /* reuse old entry and old short name for overwrite */
  449.             ssp->free_start = match - ssp->size_needed + 1;
  450.             ssp->free_size = ssp->size_needed;
  451.             ssp->slot = match;
  452.             ssp->got_slots = 1;
  453.             strncpy(dosname, dir.name, 3);
  454.             strncpy(dosname + 8, dir.ext, 3);
  455.             return ret;
  456.         } else
  457. #endif
  458.             {
  459.             dir.name[0] = (char) 0xe5;
  460.             dir_write(Dir, match, &dir);
  461.             return NAMEMATCH_RENAME;
  462.         }
  463.     }
  464.  
  465.     return ret;
  466. }
  467.  
  468.  
  469. static inline int write_slots(Stream_t *Dir,
  470.                   Stream_t *Fs,
  471.                   char *dosname, 
  472.                   char *longname,
  473.                   struct scan_state *ssp,
  474.                   write_data_callback *cb,
  475.                   void *arg,
  476.                   int Case)
  477. {
  478.     struct directory dir;
  479.  
  480.     /* write the file */
  481.     if ( ((FsPublic_t *)Fs)->fat_error)
  482.         return 0;
  483.  
  484.     if (cb(dosname, longname, arg, &dir)) {
  485.         if ((ssp->size_needed > 1) &&
  486.             (ssp->free_size >= ssp->size_needed)) {
  487.             ssp->slot = write_vfat(Dir, dosname, longname,
  488.                            ssp->free_start);
  489.             clear_vses(Dir, ssp->free_start + ssp->size_needed,
  490.                    ssp->free_start + ssp->free_size - 1);
  491.         }
  492.         dir.Case = Case & (EXTCASE | BASECASE);
  493.         dir_write(Dir, ssp->slot, &dir);
  494.     } else
  495.         return 0;
  496.  
  497.     return 1;    /* Successfully wrote the file */
  498. }
  499.  
  500. static void stripspaces(char *name)
  501. {
  502.     char *p,*non_space;
  503.  
  504.     non_space = name - 1;
  505.     for(p=name; *p; p++)
  506.         if (*p != ' ')
  507.             non_space = p;
  508.     non_space[1] = '\0';
  509. }
  510.  
  511.  
  512. int mwrite_one(Stream_t *Dir,
  513.            Stream_t *Fs,
  514.            char *progname,
  515.            char *argname,
  516.            char *shortname,
  517.            write_data_callback *cb,
  518.            void *arg,
  519.            ClashHandling_t *ch)
  520. {
  521.     char longname[VBUFSIZE], *dstname;
  522.     char dosname[13];
  523.     int expanded;
  524.     struct scan_state scan;
  525.     clash_action ret;
  526.  
  527.     expanded = 0;
  528.  
  529.     if(ch->name_converter == dos_name) {
  530.         if(shortname)
  531.             stripspaces(shortname);
  532.         if(argname)
  533.             stripspaces(argname);
  534.     }
  535.  
  536.     if(shortname){
  537.         ch->name_converter(shortname,0, &ch->use_longname, dosname);
  538.         if(ch->use_longname & 1){
  539.             /* short name mangled, treat it as a long name */
  540.             argname = shortname;
  541.             shortname = 0;
  542.         }
  543.     }
  544.                         
  545.     if (argname[0] && (argname[1] == ':')) {
  546.         /* Skip drive letter */
  547.         dstname = argname + 2;
  548.     } else {
  549.         dstname = argname;
  550.     }
  551.  
  552.     /* Copy original argument dstname to working value longname */
  553.     strcpy(longname, dstname);
  554.  
  555.     if(shortname) {
  556.         ch->name_converter(shortname,0, &ch->use_longname, dosname);
  557.         if(strcmp(shortname, longname))
  558.             ch->use_longname |= 1;
  559.     } else
  560.         ch->name_converter(longname,0, &ch->use_longname, dosname);
  561.  
  562.     ch->action[0] = ch->namematch_default[0];
  563.     ch->action[1] = ch->namematch_default[1];
  564.  
  565.     while (1) {
  566.         switch((ret=get_slots(Dir, Fs, dosname, longname,
  567.                       &scan, ch))){
  568.             case NAMEMATCH_ERROR:
  569.                 return -1;    /* Non-file-specific error, 
  570.                          * quit */
  571.                 
  572.             case NAMEMATCH_SKIP:
  573.                 return 0;    /* Skip file (user request or 
  574.                          * error) */
  575.  
  576.             case NAMEMATCH_PRENAME:
  577.                 ch->name_converter(longname,0,
  578.                            &ch->use_longname, dosname);
  579.                 continue;
  580.             case NAMEMATCH_RENAME:
  581.                 continue;    /* Renamed file, loop again */
  582.  
  583.             case NAMEMATCH_GREW:
  584.                 /* No collision, and not enough slots.
  585.                  * Try to grow the directory
  586.                  */
  587.                 if (expanded) {    /* Already tried this 
  588.                          * once, no good */
  589.                     fprintf(stderr, 
  590.                         "%s: No directory slots\n",
  591.                         progname);
  592.                     return -1;
  593.                 }
  594.                 expanded = 1;
  595.                 
  596.                 if (dir_grow(Dir, Fs, scan.max_entry)) {
  597.                     fprintf(stderr, "%s: Disk full\n",
  598.                         progname);
  599.                     return -1;
  600.                 }
  601.                 continue;
  602.             case NAMEMATCH_OVERWRITE:
  603.             case NAMEMATCH_SUCCESS:
  604.                 return write_slots(Dir, Fs, dosname, longname,
  605.                            &scan, cb, arg,
  606.                            ch->use_longname);
  607.             default:
  608.                 fprintf(stderr,
  609.                     "Internal error: clash_action=%d\n",
  610.                     ret);
  611.                 return -1;
  612.         }
  613.  
  614.     }
  615. }
  616.  
  617. void init_clash_handling(ClashHandling_t *ch)
  618. {
  619.     ch->ignore_entry = -1;
  620.     ch->nowarn = 0;    /*Don't ask, just do default action if name collision */
  621.     ch->namematch_default[0] = NAMEMATCH_AUTORENAME;
  622.     ch->namematch_default[1] = NAMEMATCH_NONE;
  623.     ch->name_converter = dos_name; /* changed by mlabel */
  624.     ch->source = -2;
  625. }
  626.  
  627. int handle_clash_options(ClashHandling_t *ch, char c)
  628. {
  629.     int isprimary;
  630.     if(isupper(c))
  631.         isprimary = 0;
  632.     else
  633.         isprimary = 1;
  634.     c = tolower(c);
  635.     switch(c) {
  636.         case 'o':
  637.             /* Overwrite if primary name matches */
  638.             ch->namematch_default[isprimary] = NAMEMATCH_OVERWRITE;
  639.             return 0;
  640.         case 'r':
  641.                 /* Rename primary name interactively */
  642.             ch->namematch_default[isprimary] = NAMEMATCH_RENAME;
  643.             return 0;
  644.         case 's':
  645.             /* Skip file if primary name collides */
  646.             ch->namematch_default[isprimary] = NAMEMATCH_SKIP;
  647.             return 0;
  648.         case 'm':
  649.             ch->namematch_default[isprimary] = NAMEMATCH_NONE;
  650.             return 0;
  651.         case 'a':
  652.             ch->namematch_default[isprimary] = NAMEMATCH_AUTORENAME;
  653.             return 0;
  654.         default:
  655.             return -1;
  656.     }
  657. }
  658.